#include <fstream>
#include "jobqueue.h"
#include "economy.h"
#include "sequences.h"

#ifdef OSX
const uint64 kAdsTemporaryLgSize = 20;
const uint64 kAdsPermanentLgSize = 20;
const uint64 kAdsQSize = (1 << 15);
#else
const uint64 kAdsTemporaryLgSize = 29;
const uint64 kAdsPermanentLgSize = 27;
const uint64 kAdsQSize = (1 << 20);
#endif

const uint64 kAdsTemporarySize = 1 << kAdsTemporaryLgSize;
const uint64 kAdsPermanentSize = 1 << kAdsPermanentLgSize;

int32 G_PROCESS_NUMBER = 0;
const int64 kRunOverhead = 100;
const int64 kCrashPenalty = 1000;
const uint64 kInitialIncome = (uint64) 5e7;
const bool kAwardUniquely = true;
const uint64 kNumEntityShards = 1000;
const double kCPUPerAd = 1000;
const uint64 kMaxTotalDemand = 100;
const int64 kEpochCPU = 100000;
const int64 kPatentTime = (int64) 1e10;

// Deal with how we store incomed entities
// The 0th level = 1 entry per kCPUPerIncome
// Each successive level = 1 entry per kCPUPerIncome * base^n
double kCPUPerIncome = 50000;
const uint64 kIncomeLevels = 2;
const uint64 kIncomeBase = 10;

// System calls
// Null means failure
Object * TEST;
Object * DATA;
Object * CREDIT;
Object * POST;
Object * ACTOR;
Object * ADVERTISE;
Object * READ;
Object * GOOD_CODE;
Object * CODE;
Object * ALL;
Object * GET_DEMAND;
Object * GET_PAYOUT;

// Used as flags
Object * INCOME;
Object * DEMAND;

Economy * G_EC;

// For debugging times
int64 G_LAST_DEBUG = 0;

void CreditAssigner::Initialize(double time, double utility) {
  history_.clear();
  history_.push_back(make_pair(time, utility));
  history_midpoint_ = 0;
  k_history_midpoint_ratio_ = 0.9;
}

double CreditAssigner::Credit(double time, double utility, bool update) {
  CHECK(history_.size() > 0);
  double improvement = utility - history_.back().second;
  if (time < history_.back().first) {
    cout << "time:" << time
	 << " hist:" << history_.back().first << endl;
    CHECK(false);
  }
  // CHECK(time >= history_.back().first);
  if (improvement <= 0) return 0;
  // we find the previous point with the greatest value of d_time / d_utility
  double max_ratio = -1;
  int32 best = -1;
  for (int32 i = (int32)history_.size()-1; i >= 0; i--) {
    double ratio = (time - history_[i].first) / (utility - history_[i].second);
    if (ratio > max_ratio) {
      max_ratio = ratio;
      best = i;
    } else break;
  }
  if (update) {
    history_.resize(best+1);
    history_.push_back(make_pair(time, utility));
    history_midpoint_ = min(history_midpoint_, (uint64)history_.size() - 1);
    while (history_[history_midpoint_].first < 
	   k_history_midpoint_ratio_ * history_.back().first) 
      history_midpoint_++;
  }
  if (history_midpoint_ != history_.size() - 1) {
    max_ratio = min(max_ratio, (time - history_[history_midpoint_].first) / 
		    (utility - history_[history_midpoint_].second));
  }
  double ret = max_ratio * improvement;
  /*cout << "time=" << time << " utility=" << utility 
       << " max_ratio=" << max_ratio << " improvement=" << improvement
       << " credit=" << ret << endl;  */
  return ret;
}

void AdStore::Insert(uint64 lhs_hash, Object *ad) {
  CHECK(GetType(ad) == ARRAY_TYPE);
  Object ** length_ptr = h_.InsertOrReturn(Fingerprint(lhs_hash, -1));
  uint64 pos = SyncFetchAddIntegerObject(length_ptr, 1);
  *(h_.InsertOrReturn(Fingerprint(lhs_hash, pos))) = ad;  
}
Object *AdStore::GetAd(uint64 lhs_hash, uint64 index) {
  Object ** look = h_.Lookup(Fingerprint(lhs_hash, index));
  if (look) return *look;
  return NULL;
}

uint64 WeightedRecent(uint64 limit, RNG *rng) {
  if (rng->Random() % 2 == 0) return rng->Random() % limit;
  return max(0ll, (int64)limit - 1 - (int64)exp(rng->RandomFraction() * log(limit)));
}

Object *AdStore::Sample(uint64 lhs_hash, RNG *rng) {
  uint64 numads = NumAds(lhs_hash);
  if (numads == 0) return NULL;
  uint64 max_trials = 20;
  for (uint64 trial = 0; trial < max_trials; trial++) { 
    // Object *look = GetAd(lhs_hash, rng->Random() % numads);
    Object *look = GetAd(lhs_hash, WeightedRecent(numads, rng));
    if (look) return look;
  }
  // cout << "Too many trials " << endl;
  return NULL;
}
uint64 AdStore::NumAds(uint64 lhs_hash) {
  Object **look = h_.Lookup(Fingerprint(lhs_hash, -1));
  if (!look) return 0;
  return GetInteger(*look);
}
void AdStore::BatchInsert(pair<uint64, Object *> * p, uint64 length) {
  if (length == 0) return;
  uint64 lhs_hash = p[0].first;
  Object ** length_ptr = h_.InsertOrReturn(Fingerprint(lhs_hash, -1));
  uint64 pos = SyncFetchAddIntegerObject(length_ptr, length);
  for (uint64 i=0; i<length; i++) 
    *(h_.InsertOrReturn(Fingerprint(lhs_hash, pos + i))) = p[i].second;  
}
void AdStore::Clear() {
  h_.JQZero(G_JQ);
}



void Economy::Init() {
  TEST = GetObjectOrMakeSymbol("TEST");
  DATA = GetObjectOrMakeSymbol("DATA");
  CREDIT = GetObjectOrMakeSymbol("CREDIT");
  POST = GetObjectOrMakeSymbol("POST");
  ACTOR = GetObjectOrMakeSymbol("ACTOR");
  ADVERTISE = GetObjectOrMakeSymbol("ADVERTISE");
  READ = GetObjectOrMakeSymbol("READ");
  GOOD_CODE = GetObjectOrMakeSymbol("GOOD_CODE");
  CODE = GetObjectOrMakeSymbol("CODE");
  INCOME = GetObjectOrMakeSymbol("INCOME");
  DEMAND = GetObjectOrMakeSymbol("DEMAND");
  ALL = GetObjectOrMakeSymbol("ALL");
  GET_DEMAND = GetObjectOrMakeSymbol("GET_DEMAND");
  GET_PAYOUT = GetObjectOrMakeSymbol("GET_PAYOUT");

  G_EC = new Economy;
}

Economy::Economy()
  :object_store_(ObjectStoreFileName()) {
  ads_[AD_PERMANENT] = new AdStore(kAdsPermanentLgSize);
  ads_[AD_TEMPORARY] = new AdStore(kAdsTemporaryLgSize);
  pthread_mutex_init(&store_lock_, NULL);
  total_cycles_ = 0;
  fake_cycles_ = 0;
  tester_ = new SequenceSearch();
  utility_ = 0.0;
  pthread_mutex_init(&credit_lock_, NULL);
  last_temporary_clear_cycles_ = 0;
  // total_income_ = 0;
  assigner_.Initialize(0, 0);
}

int32 Economy::NumShards() { return kNumEntityShards; } 

void Economy::ClearTemporaryAds() { 
  cout << "CLEARING ADS" << endl;
  ads_[AD_TEMPORARY]->Clear();
  last_temporary_clear_cycles_ = total_cycles_;
  for (uint64 i=0; i<et_.size(); i++) {
    et_[i]->ads_q_max_size_ = 1;
    et_[i]->ads_q_.clear();
  }
}

const uint64 kAdImportance = 10;
const uint64 kIncomeImportance = (1ull << 32);

void Economy::FlagReferences(int32 shard, Frontier * frontier) {
  RNG rng;
  if (G_ALLOC.urgency_ < DELETE_TEMPORARY_ADS_URGENCY) {
    uint64 begin = ShardBegin(kAdsTemporarySize, NumShards(), shard);
    uint64 end = ShardEnd(kAdsTemporarySize, NumShards(), shard);
    for (uint64 i=begin; i<end; i++) {
      Object * v = ads_[AD_TEMPORARY]->h_.ValueRef(i);
      frontier->AddObject(v);
    }
  } else {
    if (shard == 0) {
      ClearTemporaryAds();
    }
  }
  // Always add in the permanents
  {
    uint64 begin = ShardBegin(kAdsPermanentSize, NumShards(), shard);
    uint64 end = ShardEnd(kAdsPermanentSize, NumShards(), shard);
    for (uint64 i=begin; i<end; i++) {
      Object * v = ads_[AD_PERMANENT]->h_.ValueRef(i);
      frontier->AddObject(v);
    }
  }
}

void Economy::ObjectsDeleted(int32 shard) {
  for (int ad_type = 0; ad_type<2; ad_type++) {
    if (ad_type == AD_TEMPORARY && 
	G_ALLOC.urgency_ >= DELETE_TEMPORARY_ADS_URGENCY) continue;
    uint64 begin = ShardBegin(ads_[ad_type]->Size(), NumShards(), shard);
    uint64 end = ShardEnd(ads_[ad_type]->Size(), NumShards(), shard);
    for (uint64 i=begin; i<end; i++) {
      Object *& v = ads_[ad_type]->h_.ValueRef(i);
      v = NewLocation(v);
    }
  }
}

const uint64 kLargeRange = 100;

bool EconomyThread::TemporaryAdsTooFull() {
  return (e_->ads_[AD_TEMPORARY]->h_.max_look_ > 30);
}
bool EconomyThread::PermanentAdsTooFull() {
  return (e_->ads_[AD_PERMANENT]->h_.max_look_ > 30);
}
void EconomyThread::CheckClearTemporaryAds() {
  if (!TemporaryAdsTooFull()) return;
  a_->ReleaseLock();
  pthread_mutex_lock(&G_ALLOC.lock_);
  if (TemporaryAdsTooFull()) {
    for (uint32 c=0; c<G_ALLOC.thread_allocators_.size(); c++) 
      G_ALLOC.thread_allocators_[c]->AcquireLock();
    cout << "ADS TOO FULL " << endl;
    e_->ClearTemporaryAds();
    for (uint32 c=0; c<G_ALLOC.thread_allocators_.size(); c++) 
      G_ALLOC.thread_allocators_[c]->ReleaseLock();
  }
  pthread_mutex_unlock(&G_ALLOC.lock_);
  a_->AcquireLockCheckException();
}

// g: read this
void EconomyThread::CheckReloadPermanentAds() {
  if (!PermanentAdsTooFull()) return;
  a_->ReleaseLock();
  pthread_mutex_lock(&G_ALLOC.lock_);
  if (PermanentAdsTooFull()) {
    for (uint32 c=0; c<G_ALLOC.thread_allocators_.size(); c++) 
      G_ALLOC.thread_allocators_[c]->AcquireLock();
    cout << "PERMANENT ADS TOO FULL " << endl;
    kCPUPerIncome *= 2;
    cout << "Raised kCPUPerIncome to " << kCPUPerIncome << endl;
    e_->ads_[AD_PERMANENT]->Clear();
    e_->income_store_.flush();
    LoadIncome();
    for (uint32 c=0; c<G_ALLOC.thread_allocators_.size(); c++) 
      G_ALLOC.thread_allocators_[c]->ReleaseLock();
  }
  pthread_mutex_unlock(&G_ALLOC.lock_);
  a_->AcquireLockCheckException();
}

void EconomyThread::PostTemporaryAd 
(Object * entity, uint64 cpu) {
  CHECK(GetType(entity) == ARRAY_TYPE);
  uint64 h = Hash(LHS(entity));
  uint64 multiple = rng_.Round(cpu / kCPUPerAd);
  for (uint64 i=0; i<multiple; i++) {
    CheckClearTemporaryAds();
    uint64 length = e_->ads_[AD_TEMPORARY]->NumAds(h);
    if (length > 100) {
      ads_q_.push_back(make_pair(h, entity));
      if (ads_q_.size() == (int64) ads_q_max_size_) SortAdsQ();
      continue;
    }
    e_->ads_[AD_TEMPORARY]->Insert(h, entity);
  }
}

void EconomyThread::PostPermanentAd
(Object * entity, uint64 cpu) {
  CHECK(GetType(entity) == ARRAY_TYPE);
  // __sync_fetch_and_add(&e_->total_income_, cpu);

  double unit = kCPUPerIncome;
  uint64 level = 0;
  while ((level+1 < kIncomeLevels) && (unit * kIncomeBase <= cpu)) {
    unit *= kIncomeBase;
    level++;
  }
  
  uint64 multiple = rng_.Round(cpu / unit);
  if (multiple == 0) return;

  uint64 h0 = Hash(LHS(entity));
  uint64 h1 = Fingerprint(h0, level);

  for (uint64 i=0; i<multiple; i++) {
    e_->ads_[AD_PERMANENT]->Insert(h1, entity);
  }  
}

void EconomyThread::SortAdsQ() {
  // cout << "SortAdsQ size = " << ads_q_.size() << endl;
  sort(ads_q_.begin(), ads_q_.end(), LessFirst<uint64, Object *>());
  for (pair<uint64, Object *> *p = ads_q_.begin(); p<ads_q_.end(); ) {
    pair<uint64, Object *> * end = p;
    while (end < ads_q_.end() && end->first == p->first) end++;
    e_->ads_[AD_TEMPORARY]->BatchInsert(p, end-p);
    p = end;
  }
  ads_q_.clear();
  ads_q_max_size_ = min((uint64)(ads_q_max_size_ * 1.1 + 1), ads_q_.capacity_);
}

void EconomyThread::StoreIncome(Object *entity, double cpu) {
  MutexLocker locker(&e_->store_lock_);
  e_->object_store_.WriteObject(entity);
  e_->income_store_ << Hash(entity) << " " 
		    << (uint64) cpu << endl;
}

uint64 EconomyThread::LoadIncome() {
  cout << "::Read Income" << endl;
  ifstream input(e_->IncomeStoreFileName());
  uint64 h, cpu;
  uint64 max_t = 0;
  uint64 count = 0;
  while (input >> h >> cpu) {
    if (!(count & (count-1))) cout << "Income loaded: " << count << endl;
    count++;
    Object * e = e_->object_store_.HashToObject(h);
    PostPermanentAd(e, cpu);
    uint64 t = GetInteger(CreationTime(e));
    max_t = max(max_t, t);

    if (PermanentAdsTooFull()) {
      kCPUPerIncome *= 2;
      cout << "Raised kCPUPerIncome to " << kCPUPerIncome << endl;
      e_->ads_[AD_PERMANENT]->Clear();
      return LoadIncome();
    }
  }
  cout << "Income loaded max_look_=" << e_->ads_[AD_PERMANENT]->h_.max_look_ 
       << endl;
  return max_t;
}

void EconomyThread::LoadState() {
  cout << "::Read Object Store" << endl;
  e_->object_store_.Read(a_);
  uint64 max_t = LoadIncome();
  e_->total_cycles_ = max_t;
  e_->income_store_.open(e_->IncomeStoreFileName(), ios::app);
  e_->assigner_.Initialize(e_->total_cycles_, 0);
  e_->last_temporary_clear_cycles_ = e_->total_cycles_;
}

double EconomyThread::AdjustTemporary(double temp_cpu) {
  double cycles_since_clear = 
    e_->total_cycles_ - e_->last_temporary_clear_cycles_ + kEpochCPU;
  return temp_cpu * (e_->total_cycles_ / cycles_since_clear);
}

Object * EconomyThread::ReadAd(Object * lhs) {
  // -2 = none, -1 = temporary, otherwise = level
  int64 choice = -2;
  uint64 total_cpu = 0; // cpu considered so far
  uint64 h = Hash(lhs);
  uint64 nt_cpu = 
    AdjustTemporary(e_->ads_[AD_TEMPORARY]->NumAds(h) * kCPUPerAd);
  if (nt_cpu) {
    choice = -1;
    total_cpu += nt_cpu;
  }
  uint64 unit = kCPUPerIncome;
  for (uint64 level=0; level < kIncomeLevels; level++) {
    uint64 hl = Fingerprint(h, level);    
    uint64 np = e_->ads_[AD_PERMANENT]->NumAds(hl) * unit;
    total_cpu += np;
    if (rng_.RandomFraction() * total_cpu < np)
      choice = level;
    unit *= kIncomeBase;
  }
  if (choice == -2) return NULL;
  if (choice == -1) return e_->ads_[AD_TEMPORARY]->Sample(h, &rng_);
  return e_->ads_[AD_PERMANENT]->Sample(Fingerprint(h, choice), &rng_);
}

EconomyThread::EconomyThread(Economy *ec) : ads_q_(kAdsQSize) {
  e_ = ec;
  vi_ = new Vinterpreter;
  vi_->system_ = this;
  a_ = vi_->a_;
  fake_cycles_ = 0;
  ads_q_max_size_ = 1;
  in_test_ = false;
}

void EconomyThread::FlagReferences(int32 shard, Frontier *frontier) {
  RNG rng;
  if (G_ALLOC.urgency_ < DELETE_TEMPORARY_ADS_URGENCY) {
    for (int64 i=0; i<ads_q_.size(); i++) {
      frontier->AddObject(ads_q_[i].second);
    }
  }
  if (G_ALLOC.urgency_ >= KILL_THREADS_URGENCY) return;
  CHECK(false);
  //cout << "EconomyThread::FlagReferences" << endl;
  frontier->AddObject(running_);
  for (uint64 c=0; c<contributors_.size(); c++)
    frontier->AddObject(contributors_[c]);
  //cout << "end EconomyThread::FlagReferences" << endl;
}

void EconomyThread::ObjectsDeleted(int32 shard) {
  for (int64 i=0; i<ads_q_.size(); i++) {
    ads_q_[i].second = NewLocation(ads_q_[i].second);
  }
  if (G_ALLOC.urgency_ >= KILL_THREADS_URGENCY) return;
  // cout << "EconomyThread::ObjectsDeleted" << endl;
  CHECK(!WasDeleted(running_));
  running_ = NewLocation(running_);

  for (uint64 c=0; c<contributors_.size(); c++) {
    CHECK(!WasDeleted(contributors_[c]));
    contributors_[c] = NewLocation(contributors_[c]);
  }
  // cout << "end EconomyThread::ObjectsDeleted" << endl;
}

Object * EconomyThread::ReadCreateEntity(istream& in) {
  in >> ws;
  int64 income = kInitialIncome;
  int64 demand = 1;
  
  SavedObject(lhs) = ReadObject(a_, in);
  if (!lhs) return NULL;
  
  // Read flags
  while (true) {
    if (lhs == INCOME) {
      lhs = ReadObject(a_, in);
      income = GetInteger(lhs);
      lhs = ReadObject(a_, in);
    } else
    if (lhs == DEMAND) {
      lhs = ReadObject(a_, in);
      demand = GetInteger(lhs);
      lhs = ReadObject(a_, in);
    } else
      break;
  }

  in >> ws;
  SavedObject(rhs) = ReadObject(a_, in);
  if (!rhs) return NULL;
  if (income < 0) return NULL;
  Object *o_demand = MakeInteger(demand);
  Object *zero = MakeInteger(0);
  SavedObject(proto)
    = MakePrototype(a_, &NIL, &lhs, &rhs, &o_demand, &o_demand, &zero);
  PostPermanentAd(proto, income);

  return proto;
}

void EconomyThread::ReadInitialEntities(string filename) {
  ifstream initial_in(filename.c_str());
  int num = 0;
  SavedObject(e) = NULL;
  // Note: not quite sure why we need this 
  while ((e = ReadCreateEntity(initial_in))) {
    cout << EntityToString(e) << endl;
    // cout << "# " << EntityInt(e) << " " << ToString(e) << endl;
    num++;
  }
  cout << "Read in " << num << " initial entities" << endl;
}

// For now has to be a constant payout
int64 EconomyThread::Payout() {
  return 10000;
  // return 1000 + search_.SequencesFound() * 100;
}

bool EconomyThread::IsNativeMethod(Object * name) {
  return (
	  (name == TEST) ||
	  (name == DATA) || 
	  (name == CREDIT) ||
	  (name == POST) ||
	  (name == READ) ||
	  (name == GET_DEMAND) ||
	  (name == GET_PAYOUT) ||
	  false
	  );
}

Object * EconomyThread::Syscall(int32 num_parameters) {
  if (in_test_) return NULL;
  //cout << "::syscall num_parameters:" << num_parameters << endl;

  if (num_parameters < 1) return NULL;

  Object * ret = NULL;
  SavedObject(command) = vi_->GetParameter(0, num_parameters);
  // cout << "command: " << ToString(command) << endl;
  if (command == POST || command == CREDIT) {
    if (num_parameters != 4) return NULL;
    SavedObject(lhs) = vi_->GetParameter(1, num_parameters);
    SavedObject(rhs) = vi_->GetParameter(2, num_parameters);
    SavedObject(extra_demand) = vi_->GetParameter(3, num_parameters);
    ret = Post(lhs, rhs, extra_demand, command == CREDIT);
    return ret;
  }
  if (command == READ) {
    if (num_parameters != 2) return NULL;
    SavedObject(lhs) = vi_->GetParameter(1, num_parameters);
    ret = SyscallRead(lhs);
    return ret;
  }
  if (command == TEST) {
    if (num_parameters < 2) return NULL;
    SavedObject(code) = vi_->GetParameter(1, num_parameters);
    ret = Test(code);
    return ret;
  }
  if (command == DATA) {
    if (num_parameters < 2) return NULL;
    SavedObject(param) = vi_->GetParameter(1, num_parameters);
    ret = Data(param);
    return ret;
  }
  // cout << "Bad syscall " << ToString(command) << endl;
  return NIL;
  /*
    if (command == CREDIT || command == POST) {
      if (num_parameters != 4) return NULL;
      Object * lhs = vi_->GetParameter(1, num_parameters);
    Object * rhs = vi_->GetParameter(2, num_parameters);
    Object * extra_demand = vi_->GetParameter(3, num_parameters);
    ret = Post(lhs, rhs, extra_demand, (command == CREDIT));
    return ret;
  }
  if (command == GET_DEMAND) {
    if (num_parameters != 1) return NULL;
    return MakeIntegerObject(ComputeContributorsDemand(NULL));
  }
  if (command == GET_PAYOUT) {
    if (num_parameters != 1) return NULL;
    return MakeIntegerObject(Payout());
  } 
  */

  return ret;
}

void EconomyThread::ForgetSince(int64 old_clock) {
  while (contributors_.size() && (contributor_times_.back() >= old_clock)) {
    contributor_times_.pop_back();
    contributors_.pop_back();
  }
}

// Return -1 if total demand is > max demand
int64 EconomyThread::FlattenContributors(vector<Object *> * flat) {
  int64 total_demand = 0;
  set<Object *> counted;
  vector<Object *> uncounted;
  if (contributors_.size()) uncounted.push_back(contributors_.back());
  // = contributors_;
  while (uncounted.size()) {
    Object * o = uncounted.back();
    uncounted.pop_back();
    if (counted % o) continue;
    counted.insert(o);
    if (GetInteger(SelfDemand(o))) {
      total_demand += GetInteger(SelfDemand(o));
      if (total_demand > (int64)kMaxTotalDemand) {
	// cout << "Exceeded total demand" << endl;
	return -1;
      }
      flat->push_back(o);
    }
    if (GetInteger(CreationTime(o)) + kPatentTime < e_->total_cycles_)
      continue;
    Object * contributors = Contributors(o);
    if (contributors != NIL) {
      for (uint32 c=0; c<contributors->ArraySize(); c++) {
	uncounted.push_back(contributors->Ref(c));
      }
    }
  }
  return total_demand;
}

// pass in the flattened contributors
int64 EconomyThread::ComputeTotalDemand(const vector<Object *> & flat) {
  int64 ret = 0;
  for (uint64 i=0; i<flat.size(); i++) {
    ret += GetInteger(SelfDemand(flat[i]));
  }
  return ret;
}


Object * EconomyThread::Post(Object *& lhs, 
			     Object *& rhs,
			     Object *& self_demand,
			     bool credit) {


  // Make sure deep objects don't screw us up
  const int64 MAX_DEPTH = 1000;
  int64 lnc = NodeCount(lhs);
  int64 rnc = NodeCount(rhs);
  if (lnc == 0 || rnc == 0) return NIL;
  vi_->AdvanceClock(lnc + rnc);
  if (vi_->Overtime()) return NIL;  

  if (!DepthLessThan(lhs, MAX_DEPTH)) return NIL;
  if (!DepthLessThan(rhs, MAX_DEPTH)) return NIL;

  // Figure out the total demand
  if (GetType(self_demand) != INTEGER) return NIL;
  int64 self_demand_i = GetInteger(self_demand);
  if (self_demand_i < 0) return NIL;
  
  uint64 contributor_total_demand_i = 
    GetInteger(TotalDemand(contributors_.back()));
  uint64 total_demand_i = contributor_total_demand_i + self_demand_i;

  // Figure out the cash gift and advance it
  int64 cash_gift = vi_->InstructionsRemaining();
  if (!credit) {
    if (cash_gift <= 0) return NIL;
    vi_->AdvanceClock(cash_gift);
    fake_cycles_ += cash_gift;
  }
  CHECK(contributors_.size() > 0);
  
  SavedObject(contributor_array) = a_->MakeArray(1, &contributors_.back());
  
  Object * total_demand_o = MakeInteger(total_demand_i);
  Object * creation_time = MakeInteger(e_->total_cycles_);
  SavedObject(ad_proto) = MakePrototype
    (a_, &contributor_array, &lhs, &rhs, 
     &total_demand_o, &self_demand,
     &creation_time);

  if (credit) {
    AddContributor(ad_proto);
    return NIL;
  }

  PostTemporaryAd(ad_proto, cash_gift);

  if (false)
    cout << "::Post lhs:" << ToString(lhs) 
	 << " rhs:" << ToString(rhs)
	 << " cash:" << cash_gift
	 << " self_demand:" << ToString(self_demand) << endl;
  return NIL;
}

Object * EconomyThread::Data(Object *& param) {
  Object * ret = e_->tester_->Data(param, a_);
  if (ret) return ret;
  return NIL;
}

Object * EconomyThread::Test(Object *& code) {
  Testing t(this);

  // Does it help at all?
  double diff = e_->tester_->Test(code, vi_, false);
  if (diff == 0.0) return NIL;

  // Figure out what the total demand is
  // Make sure it doesn't exceed max demand 
  // (handled by flattencontributors)
  SavedObjectVector(flat);
  int64 total_demand = FlattenContributors(&flat);
  if (total_demand == -1) 
    return NIL;

  // Let's not print the total demand
  // cout << "Total demand:" << total_demand << endl;

  // Ok now credit this thing for real
  diff = e_->tester_->Test(code, vi_, true);
  double credit = 0.0;
  {
    pthread_mutex_lock(&e_->credit_lock_);
    e_->utility_ += diff;
    credit = e_->assigner_.Credit(e_->total_cycles_, e_->utility_, true);
    pthread_mutex_unlock(&e_->credit_lock_);
  }
  if (credit == 0.0) return NIL;

  // Now distribute the income
  CHECK(total_demand > 0);
  double total_income = credit;
  for (uint64 i=0; i<flat.size(); i++) {
    double d_reward = total_income * 
      (double) GetInteger(SelfDemand(flat[i])) / (double) kMaxTotalDemand;
    StoreIncome(flat[i], d_reward);
    PostPermanentAd(flat[i], d_reward);
    CheckReloadPermanentAds();
  }  
  return NIL;
}

Object * EconomyThread::SyscallRead(Object *& lhs) {
  // Read from the income if possible?
  SavedObject(read) = ReadAd(lhs);
  // cout << "Read ad " << ToString(lhs) << " -> " 
  // << (read ? ToString(read) : string("")) << endl; 
  if (read == NIL || read == NULL) return NIL;
  if ((GetType(read) != ARRAY_TYPE) || 
      (read->ArraySize() != NUM_ENTITY_FIELDS) ) {
    cout << "ReadInternal returns " << ToString(read) << endl;
    CHECK(false);
  }
  //if (false) 
  //cout << "::Read lhs=" << ToString(lhs) 
  //     << " read=" << ToString(read)
  //     << " rhs=" << ToString(RHS(read)) << endl;
  AddContributor(read);
  CHECK(RHS(read));
  return RHS(read);
}

/*
int32 EconomyNum(int64 clock) {
  double epoch = 1 + max ((double)clock, 0.0) / 1e9;
  double l = log (epoch) * 10;
  return int32 (l);
}
*/

void EconomyThread::Loop(int64 max_cpu) {
  while (1) {
    try {
      RunEntity();
      Epoch();
    } catch (ThreadAllocatorException e) {
      // cout << "Thread killed" << endl;
    }
  }
}

// Print immutable information about an entity
string EconomyThread::EntityToString(Object *& e) {  
  ostringstream ostr;
  ostr << LHS(e) << " -> " << RHS(e) 
       << " total_demand: " << ToString(TotalDemand(e))
       << " demand: " << ToString(SelfDemand(e))
       << " creation_time: " << ToString(CreationTime(e));
  if (Contributors(e) == NIL) {
    ostr << "  no contributors" << endl;
  }
  return ostr.str();
}

void EconomyThread::OutputEconomy(ostream & out, 
				  int64 income_limit) {
  hash_map<Object *, uint64, PointerHash<Object> > income;
  NLHash<Object *> & h = e_->ads_[AD_PERMANENT]->h_;
  for (uint64 i=0; i<h.Size(); i++) {
    Object * e = h.ValueRef(i);
    if (h.KeyRef(i) && e && GetType(e) == ARRAY_TYPE) {
      income[e]++;
    }
  }
  vector<pair<Object *, uint64> > v(income.begin(), income.end());
  sort(v.begin(), v.end(), GreaterSecond<Object *, uint64>());
  for (uint64 i=0; i<v.size(); i++) {
    Object * e = v[i].first;
    uint64 income = v[i].second;
    if ((int64)income < income_limit) break;
    out << ToString(LHS(e)) << " -> (" << income << ") " << ToString(RHS(e))
	<< " demand: " << GetInteger(SelfDemand(e)) << " creation_time: " 
	<< (GetInteger(CreationTime(e)) / 1e9) << "B" << endl;
  }
}

// Output for debugging what's going on,
// Don't use memory address based output
void EconomyThread::DebugOutputEconomy(ostream & out) {
  /*  ReadLocker rl(this); rl.Go();
  // Print all entities
  for (uint32 shard = 0; shard < kNumEntityShards; shard++) {
    forall(run, e_->hash_entity_[shard]) {
      Object * e = run->second.entity_;
      out << " hash: " << Hash(e) 
	  << " income: " << run->second.income_ 
	  << " lhs: " << ToString(LHS(e)) 
	  << " rhs: " << ToString(RHS(e)) << endl;
    }
    }*/
}

int64 EconomyThread::RandomCPU() {
  uint32 exp = 
    (uint32) (log (1 / max ((double) rng_.RandomFraction(), 1e-4)) / log(2));
  return 1000LL << exp;
}
 
// Run an actor and try and do something
// if entity_int == -1 chooses an entity randomly and runs it until
// the alloted cpu runs out
// if entity_int != -1, picks that entity and runs it up to once,
// but not exceeding the cpu limit

Object * EconomyThread::RunEntity(Object * chosen_entity) {
  SavedObject(return_value) = NULL;

  // Move it into the running actor
  if (chosen_entity != NULL) {
    running_ = chosen_entity;
  } else {
    int32 num_trials = 0;
    while (1) {
      running_ = ReadAd(CODE);
      num_trials++;
      if (running_) break;
    }
  }
  
  SavedObject(code) = RHS(running_);

  int64 cash_limit = 0;
  if (!chosen_entity) cash_limit = RandomCPU();
  else cash_limit = 1000000;

  // total_cycles_ += cash_limit;
  // cycles_since_cash_clear_ += cash_limit;

  //cout << "::RunEntity " << EntityInt(running_) << endl;
  //cout << " cash_limit:" << cash_limit << endl;

  while (cash_limit > 0) {
    cash_limit -= kRunOverhead;
    if (cash_limit <= 0) break;
    ClearContributors();
    AddContributor(running_);
    
    int64 old_clock = vi_->Clock();

    // Run it
    return_value = vi_->SafeExecute(code, cash_limit);
    int64 cash_used = vi_->Clock() - old_clock;
    cash_limit -= cash_used;
    if (!return_value) cash_limit -= kCrashPenalty;
    
    // Here we were run directly
    if (chosen_entity) break;
  }

  return return_value;
}

void DebugCycles(uint64 old, uint64 newcycles, uint64 oldfake, uint64 newfake) {
  uint64 old_real = old - oldfake;
  uint64 new_real = newcycles - newfake;
  
  uint64 NM = 3000;
  if (old_real / (NM * 1000000) != new_real / (NM * 1000000)) {
    uint64 now = PortableTime();
    cout << "**************** " 
	 << (new_real / 1000000)
	 << "M cycles "
	 << (oldfake / 1000000) << "M fakes "
	 << "time: " << (now - G_LAST_DEBUG) 
	 << endl;
    G_LAST_DEBUG = now;    
    // exit(0);
  }
}

void EconomyThread::Epoch() {
  if (vi_->Clock() < kEpochCPU) return;
  uint64 cycles = vi_->Clock();
  vi_->SetClock(0);
  double old_total_cycles = __sync_fetch_and_add(&e_->total_cycles_, cycles);
  double old_fake_cycles = __sync_fetch_and_add(&e_->fake_cycles_, fake_cycles_);
  double new_total_cycles = old_total_cycles + cycles;
  DebugCycles(old_total_cycles, new_total_cycles, 
	      old_fake_cycles, old_fake_cycles + fake_cycles_);
  fake_cycles_ = 0;
  CHECK(new_total_cycles >= old_total_cycles);
}

void EconomyThread::ClearContributors() { 
  contributors_.clear(); 
  contributor_times_.clear();
}

void EconomyThread::AddContributor(Object *&c) {
  if (contributors_.size()) {
    SavedObject(contrib) = a_->MakeArray(2, &c, &contributors_.back()); 
    uint64 total_demand = 
      GetInteger(TotalDemand(c))
      + GetInteger(TotalDemand(contributors_.back()));
    Object * zero = MakeInteger(0);
    Object * total_demand_o = MakeInteger(total_demand);
    Object * creation_time = MakeInteger(e_->total_cycles_);
    contributors_.push_back
      (MakePrototype(a_, &contrib, &NIL, &NIL, &total_demand_o, 
		     &zero, &creation_time));
  } else {
    contributors_.push_back(c);
  } 
  contributor_times_.push_back(vi_->Clock());
}

int credit_main() {
  CreditAssigner ca;
  double t, u;
  while (cin >> t >> u) {
    cout << ca.Credit(t, u, true) << endl;
  }
  return 0;
}

int economy_main() {
  /* DISABLED
  InitEnumObjects();
  vi_->LoadFile("library", false);
  Economy::Init();
  Economy ec;
  ec.ReadInitialEntities("entities");
  ec.CatchUp();
  ec.Loop(0);
  */
  return 0;
}

void * LoopEconomyThread(void *economy_thread) {
  EconomyThread * et = (EconomyThread *)economy_thread;
  et->Loop(-1);
  cout << "Why did the loop terminate?" << endl;
  CHECK(false);
  return NULL;
}
void Economy::MakeThreads(int n) {
  CHECK(et_.size() == 0);
  num_threads_ = n;
  for (int i=0; i<n; i++) {
    EconomyThread * t = new EconomyThread(this);
    t->id_ = i;
    et_.push_back(t);
  } 
}
void Economy::StartThreads() {
  for (uint64 i=0; i<et_.size(); i++) {
    EconomyThread * t = et_[i];
    int return_code = pthread_create(&t->my_thread_, NULL, LoopEconomyThread, t);
    if (return_code) {
      cout << "Error creating thread - return code " << return_code << endl;
    }
  }
}

void load_economy_main(int32 n_threads) {
  InitLogCache();
  InitJobQueue(n_threads);
  InitInterpret();
  Economy::Init();
  G_LAST_DEBUG = PortableTime();
  G_EC->MakeThreads(n_threads);
  EconomyThread * et = G_EC->et_[0];
  Vinterpreter *vi = et->vi_;
  vi->LoadFile("library", false);
  et->ReadInitialEntities("entities");
  et->LoadState();
  ((SequenceSearch *) G_EC->tester_)->LoadState(et->a_);
}

int loop_economy_main(int32 n_threads) {
  load_economy_main(n_threads);
  G_EC->StartThreads();
  cout << "Started Threads!!!!" << endl;
  sleep(100000000);
  pthread_exit(NULL);
  return 0;
}

int output_economy_main(int32 n_threads) {
  load_economy_main(n_threads);
  EconomyThread * et = G_EC->et_[0];
  ofstream output("economy");
  et->OutputEconomy(output, 0);
  return 0;
}

int interpret_economy_main(int32 n_threads) {
  load_economy_main(n_threads);
  EconomyThread * et = G_EC->et_[0];
  Object * code;
  Object * demand = MakeInteger(1);
  Object * creation_time = MakeInteger(G_EC->total_cycles_);
  while ((code = ReadObject(et->a_, cin))) {
    Object * entity = MakePrototype(et->a_, &NIL, &CODE, &code, 
				    &demand, &demand, &creation_time);
    Object * ret = et->RunEntity(entity);
    cout << ToString(ret) << endl;
  }
  return 0;
}

int main(int argc, char ** argv) {
  // if (argc > 2) G_PROCESS_NUMBER = atoi(argv[2]);
  // if (G_PROCESS_NUMBER == 0) award_log.open("awards");
  
  // int32 sr = G_PROCESS_NUMBER; 
  // sr += (int32)(time(0));
  // cout << "random seed " << sr << endl;
  // srand(sr);

  srand(time(0));
  string command;
  if (argc > 1) command = argv[1];
  if (command == "in") {
    return interpret_main();
  }
  if (command == "credit") {
    return credit_main();
  }

  if (command == "loop") {
    return loop_economy_main(atoi(argv[2]));
  }

  if (command == "index") { 
    MakeBinarySequenceFile();
    return 0;
  }

  if (command == "output") {
    return output_economy_main(atoi(argv[2]));
  }

  if (command == "ie") {
    return interpret_economy_main(atoi(argv[2]));
  }

  return loop_economy_main(atoi(argv[1]));
}
